﻿using System;
using System.Windows;

namespace VisualView.Axis
{
    /// <summary>
    /// 数字轴
    /// Value：实际值
    /// AxisValue：转换算法后的坐标值
    /// Location：像素位置值
    /// </summary>
    public abstract class BaseNumberAxis : BaseAxis<double>
    {
        /// <summary>
        /// 数值显示范围
        /// </summary>
        public NumberAxisRange Range
        {
            get { return (NumberAxisRange)GetValue(RangeProperty); }
            set { SetValue(RangeProperty, value); }
        }
        public static readonly DependencyProperty RangeProperty = DependencyProperty.Register("Range", typeof(NumberAxisRange), typeof(BaseNumberAxis), new PropertyMetadata(RangeChangedNeedRefresh));

        public virtual void SetRange(double min, double max)
        {
            this.Range = new NumberAxisRange(min, max);
        }

        /// <summary>
        /// 范围最小值
        /// </summary>
        public double RangeMin
        {
            get { return (double)GetValue(RangeMinProperty); }
            set { SetValue(RangeMinProperty, value); }
        }
        public static readonly DependencyProperty RangeMinProperty = DependencyProperty.Register("RangeMin", typeof(double), typeof(BaseNumberAxis), new PropertyMetadata(double.MinValue));

        /// <summary>
        /// 范围最大值
        /// </summary>
        public double RangeMax
        {
            get { return (double)GetValue(RangeMaxProperty); }
            set { SetValue(RangeMaxProperty, value); }
        }
        public static readonly DependencyProperty RangeMaxProperty = DependencyProperty.Register("RangeMax", typeof(double), typeof(BaseNumberAxis), new PropertyMetadata(double.MaxValue));

        #region 数值转换
        /// <summary>
        /// 获取实际值和坐标值转换参数
        /// </summary>
        /// <returns></returns>
        public virtual object GetAxisValueConvertParam() { return null; }

        /// <summary>
        /// 获取实际值、坐标值转换像素值参数
        /// </summary>
        /// <returns></returns>
        public override AxisValueLocationConvertParam GetValueLocationParam()
        {
            var p = GetAxisValueConvertParam();
            var range = this.Range;
            return new NumberAxisValueLocationConvertParam(range.Max, range.Min, ValueToAxisValue(range.Max, p), ValueToAxisValue(range.Min, p), base.GetLength(), AxisType, p);
        }

        /// <summary>
        /// 实际值转坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public abstract double ValueToAxisValue(double value, object param);
        public virtual double ValueToAxisValue(double value) { return ValueToAxisValue(value, GetAxisValueConvertParam()); }
        /// <summary>
        /// 坐标值转实际值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public abstract double AxisValueToValue(double value, object param);
        public virtual double AxisValueToValue(double value) { return AxisValueToValue(value, GetAxisValueConvertParam()); }

        /// <summary>
        /// 获取实际值对应的像素位置
        /// </summary>
        /// <param name="value"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public override double GetValueLocation(double value, AxisValueLocationConvertParam param)
        {
            if (param is NumberAxisValueLocationConvertParam np)
            {
                value = ValueToAxisValue(value, np.AxisValueConvertParam);
                return GetAxisValueLocation(value, np);
            }
            else return double.NaN;
        }
        /// <summary>
        /// 获取坐标数字对应的像素位置
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value)
        {
            if (!IsLoaded) return default;
            return GetAxisValueLocation(value, GetValueLocationParam());
        }
        /// <summary>
        /// 获取坐标数据对应的像素位置
        /// </summary>
        /// <param name="value"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value, AxisValueLocationConvertParam param)
        {
            if (param is NumberAxisValueLocationConvertParam np)
            {
                var temp = (value - np.MinAxisValue) * np.LengthDifferencePercent;
                return (np.AxisType == AxisType.X || np.AxisType == AxisType.X2) ? temp : np.Length - temp;
            }
            else return double.NaN;
        }
        /// <summary>
        /// 获取像素点位对应的坐标值
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double location)
        {
            if (!IsLoaded) return default;
            return GetLocationAxisValue(location, GetValueLocationParam());
        }
        /// <summary>
        /// 获取像素点位对应的坐标值
        /// </summary>
        /// <param name="location"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double location, AxisValueLocationConvertParam param)
        {
            if (param is NumberAxisValueLocationConvertParam np) return ((np.AxisType == AxisType.X || np.AxisType == AxisType.X2) ? location : (np.Length - location)) / np.LengthDifferencePercent + np.MinAxisValue;
            return double.NaN;
        }

        /// <summary>
        /// 获取像素点位对应的实际值
        /// </summary>
        /// <param name="location"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public override double GetLocationValue(double location, AxisValueLocationConvertParam param)
        {
            if (param is NumberAxisValueLocationConvertParam np) return AxisValueToValue(GetLocationAxisValue(location, np), np.AxisValueConvertParam);
            else return double.NaN;
        }
        #endregion

        #region 分隔间距计算
        protected virtual void GetAxisStartAndPace(NumberAxisValueLocationConvertParam param, out double start, out double pace)
        {
            var tempmax = param.MaxAxisValue.ToString();
            var tempmin = param.MinAxisValue.ToString();
            int i = 0;
            while (tempmax.Length > i && tempmin.Length > i && tempmax[i] == tempmin[i]) i++;
            var count = Convert.ToInt32(param.Length / (Math.Max(i, 3) * FontSize));
            pace = GetPace(param.AxisValueDifference, count);
            start = param.MinAxisValue % pace != 0d ? (param.MinAxisValue - param.MinAxisValue % pace) : param.MinAxisValue;
        }
        #endregion

        #region 轴范围移动缩放
        public override void MoveRange(double x, double y)
        {
            double Length = 0;
            double MoveLength = 0;
            switch (AxisType)
            {
                case AxisType.X:
                case AxisType.X2:
                    Length = this.ActualWidth;
                    MoveLength = x;
                    break;
                case AxisType.Y:
                case AxisType.Y2:
                    Length = this.ActualHeight;
                    MoveLength = y;
                    break;
                default:
                    break;
            }
            if (MoveLength == 0) return;
            var param = GetAxisValueConvertParam();
            var axisMax = ValueToAxisValue(Range.Max, param);
            var axisMin = ValueToAxisValue(Range.Min, param);
            var step = (axisMax - axisMin) * MoveLength / Length;
            var min = AxisValueToValue(axisMin - step, param);
            var max = AxisValueToValue(axisMax - step, param);
            SetRange(Math.Max(RangeMin, min), Math.Min(RangeMax, max));
        }

        public override void ZoomRange(Point center, double zoom)
        {
            double CenterLength = 0;
            switch (AxisType)
            {
                case AxisType.X:
                case AxisType.X2:
                    CenterLength = center.X;
                    break;
                case AxisType.Y:
                case AxisType.Y2:
                    CenterLength = center.Y;
                    break;
                default:
                    break;
            }
            var param = GetValueLocationParam() as NumberAxisValueLocationConvertParam;
            var centerAxisValue = GetLocationAxisValue(CenterLength, param);
            var axisMax = ValueToAxisValue(Range.Max, param.AxisValueConvertParam);
            var axisMin = ValueToAxisValue(Range.Min, param.AxisValueConvertParam);
            var newAxisMax = (axisMax - centerAxisValue) * zoom + centerAxisValue;
            var newAxisMin = (axisMin - centerAxisValue) * zoom + centerAxisValue;
            var min = AxisValueToValue(newAxisMin, param.AxisValueConvertParam);
            var max = AxisValueToValue(newAxisMax, param.AxisValueConvertParam);
            SetRange(Math.Max(RangeMin, min), Math.Min(RangeMax, max));
        }
        #endregion
    }


    /// <summary>
    /// 数字轴显示范围区间
    /// </summary>
    public struct NumberAxisRange
    {
        /// <summary>
        /// 最小值
        /// </summary>
        public double Min { get; set; }
        /// <summary>
        /// 最大值
        /// </summary>
        public double Max { get; set; }
        public NumberAxisRange(double min, double max)
        {
            this.Min = Math.Min(min, max);
            this.Max = Math.Max(min, max);
        }
        public NumberAxisRange(double max) : this(0d, max) { }
        public NumberAxisRange() : this(10000d) { }
    }

    /// <summary>
    /// 轴转换参数，跨线程使用
    /// </summary>
    public class NumberAxisValueLocationConvertParam : AxisValueLocationConvertParam
    {
        public double MaxValue { get; protected set; }
        public double MinValue { get; protected set; }
        /// <summary>
        /// 最大坐标值
        /// </summary>
        public double MaxAxisValue { get; protected set; }
        /// <summary>
        /// 最小坐标值
        /// </summary>
        public double MinAxisValue { get; protected set; }
        /// <summary>
        /// 轴范围区间大小
        /// </summary>
        public double AxisValueDifference { get; protected set; }
        /// <summary>
        /// 轴长度与轴范围的比值
        /// </summary>
        public double LengthDifferencePercent { get; protected set; }
        /// <summary>
        /// 实际值和坐标值转换参数
        /// </summary>
        public object AxisValueConvertParam { get; protected set; }

        public NumberAxisValueLocationConvertParam(double maxValue, double minValue, double maxAxisValue, double minAxisValue, double length, AxisType axisType, object axisValueConvertParam) : base(length, axisType)
        {
            MaxValue = maxValue;
            MinValue = minValue;
            MaxAxisValue = maxAxisValue;
            MinAxisValue = minAxisValue;
            AxisValueDifference = maxAxisValue - minAxisValue;
            LengthDifferencePercent = length / AxisValueDifference;
            AxisValueConvertParam = axisValueConvertParam;
        }
    }
}
